//
//  PMSegmentShape.m
//  PezMonkey
//
//  Created by Spence DiNicolantonio on 10/29/09.
//  Copyright 2009 Necrosoft. All rights reserved.
//

/**
 * A subclass of PMShape for representing line segment collision shapes. Note that
 * at this time, PMSegmentShapes cannot collide with other PMSegmentShapes. They
 * are intended for use as a static shape.
 */


#import "PMSegmentShape.h"
#import "PMBody.h"


#pragma mark Private Interface
@interface PMSegmentShape (Private)
- (void)updateNormal;
@end
#pragma mark -


@implementation PMSegmentShape


#pragma mark -
#pragma mark Init

/**
 * Returns a PMSegmentShape object initialized to correspond to a given PMBody 
 * object, with a width of 0.0, and start and end points defined by a given 
 * segment, relative to the body's center of gravity.
 */
- (id)initWithBody:(PMBody *)aBody segment:(PMSegment)segment {
	return [self initWithBody:aBody 
						start:segment.start 
						  end:segment.end 
						width:0.0];
}
	
/**
 * Returns a PMSegmentShape object initialized to correspond to a given PMBody 
 * object, with a given width, and start and end points defined by a given segment, 
 * relative to the body's center of gravity.
 */
- (id)initWithBody:(PMBody *)aBody segment:(PMSegment)segment width:(PMFloat)width {
	return [self initWithBody:aBody 
						start:segment.start 
						  end:segment.end 
						width:width];
}

/**
 * Returns a PMSegmentShape object initialized to correspond to a given PMBody 
 * object, with given start and end points relative to the body's center of gravity 
 * and width of 0.0.
 */
- (id)initWithBody:(PMBody *)aBody start:(PMVect)start end:(PMVect)end {
	return [self initWithBody:aBody start:start end:end width:0.0];
}

/**
 * Returns a PMSegmentShape object initialized to correspond to a given PMBody
 * object, with given start and end points relative to the body's center of gravity 
 * and given width.
 */
- (id)initWithBody:(PMBody *)aBody start:(PMVect)start end:(PMVect)end width:(PMFloat)width {
	if (![super init])
		return nil;
	
	// allocate and initialize cpShape
	shape = cpSegmentShapeNew(aBody.cpBody, start, end, width/2.0);
	self.body = aBody;
	
	// reference this PMShape object with cpShape's data pointer
	// this is for internal use
	shape->data = self;
	
	return self;
}


#pragma mark Convenience

/**
 * Creates and returns a PMSegmentShape object initialized to correspond to a 
 * given PMBody object, with a width of 0.0, and start and end points defined by 
 * a given segment, relative to the body's center of gravity.
 */
+ (PMSegmentShape *)segmentShapeWithBody:(PMBody *)aBody segment:(PMSegment)segment {
	return [[[PMSegmentShape alloc] initWithBody:aBody 
										 segment:segment] autorelease];
}

/**
 * Creates and returns a PMSegmentShape object initialized to correspond to a 
 * given PMBody object, with a given width, and start and end points defined by 
 * a given segment, relative to the body's center of gravity.
 */
+ (PMSegmentShape *)segmentShapeWithBody:(PMBody *)aBody segment:(PMSegment)segment width:(PMFloat)width {
	return [[[PMSegmentShape alloc] initWithBody:aBody 
										 segment:segment
										   width:width] autorelease];
}

/**
 * Creates and returns a PMSegmentShape object initialized to correspond to a 
 * given PMBody object, with given start and end points relative to the body's 
 * center of gravity and width of 0.0.
 */
+ (PMSegmentShape *)segmentShapeWithBody:(PMBody *)aBody start:(PMVect)start end:(PMVect)end {
	return [[[PMSegmentShape alloc] initWithBody:aBody 
										   start:start 
											 end:end] autorelease];
}

/**
 * Creates and returns a PMSegmentShape object initialized to correspond to a 
 * given PMBody object, with given start and end points relative to the body's 
 * center of gravity and given width.
 */
+ (PMSegmentShape *)segmentShapeWithBody:(PMBody *)aBody start:(PMVect)start end:(PMVect)end width:(PMFloat)width {
	return [[[PMSegmentShape alloc] initWithBody:aBody 
										   start:start 
											 end:end 
										   width:width] autorelease];
}


#pragma mark -
#pragma mark Getters

/**
 * Returns the receiver's starting point.
 */
- (PMVect)start {
	return (PMVect) ((cpSegmentShape *) shape)->a;
}

/**
 * Returns the receiver's end point.
 */
- (PMVect)end {
	return (PMVect) ((cpSegmentShape *) shape)->b;
}

/**
 * Returns a PMSegment structure defining the receiver's start and end points.
 */
- (PMSegment)segment {
	return PMSegmentMake([self start], [self end]);
}

/**
 * Returns the receiver's normal vector (Using the right-hand rule for direction).
 */
- (PMVect)normal {
	return (PMVect) ((cpSegmentShape *) shape)->n;
}

/**
 * Returns the receivers width.
 */
- (PMFloat)width {
	return ((cpSegmentShape *) shape)->r * 2.0;
}


#pragma mark -
#pragma mark Setters

/**
 * Sets the receiver's start point to a given position. Use of this method is not 
 * recommended, as it could result in anomalous behavior.
 */
- (void)setStart:(PMVect)start {
	[self setStart:start end:[self end]];
}

/**
 * Sets the receiver's end point to a given position. Use of this method is not 
 * recommended, as it could result in anomalous behavior.
 */
- (void)setEnd:(PMVect)end {
	[self setStart:[self start] end:end];
}

/**
 * Sets the receiver's start and end points to those described by a given PMSegment
 * structure.
 */
- (void)setSegment:(PMSegment)segment {
	[self setStart:segment.start end:segment.end];
}

/**
 * Sets the receivers end points to two given positions. Use of this method is not 
 * recommended, as it could result in anomalous behavior.
 */
- (void)setStart:(PMVect)start end:(PMVect)end {
	cpSegmentShapeSetEndpoints(shape, start, end);
}

/**
 * Sets the receiver's width to a given value. Use of this method is not recommended,
 * as it could result in anomalous behavior.
 */
- (void)setWidth:(PMFloat)width {
	cpSegmentShapeSetRadius(shape, width / 2.0);
}

@end
